//
//  MidiMessage.h
//  MusicAnalyzer
//
//  Created by jiro on 12/03/23.
//  Copyright 2012 jiro music. All rights reserved.
//

#pragma once

#include <string>

#include "MidiNote.h"

namespace sc
{
	class MidiMessage
	{
	public:

		enum FirstCategory
		{
			//	 first category
			FirstCategory_Channel,
			FirstCategory_System,
			
			FirstCategory_Unknown
		};
		
		enum SecondCategory
		{
			//	channel category
			SecondCategory_Voice,
			SecondCategory_Mode,

			//	system category
			SecondCategory_Exclusive,
			SecondCategory_Common,
			SecondCategory_RealTime,
			
			SecondCategory_Unknown,
		};

		enum Type
		{
			//	voice category
			Type_NoteOn					= 0x90,
			Type_NoteOff				= 0x80,
			Type_PolyphonicKeyPressure	= 0xA0,
			Type_ControlChange			= 0xB0,
			Type_ProgramChange			= 0xC0,
			Type_ChannelPressure		= 0xD0,
			Type_PitchBendChange		= 0xE0,
	
			//	mode category
			/*	@remark byte format example.
				when input data is
					0xB0 0x78 0x00,
				enumurator value is
					0x78B0.
				value is ignored.			*/
			Type_AllSoundOff			= 0x78B0,
			Type_ResetAllController		= 0x79B0,
			Type_LocalControl			= 0x7AB0,
			Type_AllNoteOff				= 0x7BB0,
			Type_OmniOn					= 0x7CB0,
			Type_OmniOff				= 0x7DB0,
			Type_PolyphonicOn			= 0x7EB0,
			Type_MonophonicOn			= 0x7FB0,

			//	system category
			Type_Exclusive				= 0xF0,

			//	common category
			Type_QuarterFrame			= 0xF1,
			Type_SongPositionPointer	= 0xF2,
			Type_SongSelect				= 0xF3,
			Type_TuneRequest			= 0xF6,
			Type_EndOfExclusive			= 0xF7,

			//	real time category
			Type_TimingClock			= 0xF8,
			Type_Start					= 0xFA,
			Type_Continue				= 0xFB,
			Type_Stop					= 0xFC,
			Type_ActiveSencing			= 0xFE,
			Type_SystemReset			= 0xFF,
			
			Type_Unknown = 0x00,

		};

		enum ControlChangeType
		{
			ControlChangeType_BankSelect = 0x00,
			ControlChangeType_ModulationWheelOrLever = 0x01,
			ControlChangeType_BreathController = 0x02,
			ControlChangeType_FootController = 0x04,
			ControlChangeType_PortamentoTime = 0x05,
			ControlChangeType_ChannelVolume = 0x07,
			ControlChangeType_Balance = 0x08,
			ControlChangeType_Pan = 0x0A,
			ControlChangeType_ExpressionController = 0x0B,
			ControlChangeType_EffectControl1 = 0x0C,
			ControlChangeType_EffectControl2 = 0x0D,
			ControlChangeType_GeneralPurposeController1 = 0x10,
			ControlChangeType_GeneralPurposeController2 = 0x11,
			ControlChangeType_GeneralPurposeController3 = 0x12,
			ControlChangeType_GeneralPurposeController4 = 0x13,
			ControlChangeType_LSBForControl0_BankSelect = 0x20,
			ControlChangeType_LSBForControl1_ModulationWheelOrLever = 0x21,
			ControlChangeType_LSBForControl2_BreathController = 0x22,
			ControlChangeType_LSBForControl3_Undefined = 0x23,
			ControlChangeType_LSBForControl4_FootController = 0x24,
			ControlChangeType_LSBForControl5_PortamentoTime = 0x25,
			ControlChangeType_LSBForControl6_DataEntry = 0x26,
			ControlChangeType_LSBForControl7_ChannelVolume = 0x27,
			ControlChangeType_LSBForControl8_Balance = 0x28,
			ControlChangeType_LSBForControl9_Undefined = 0x29,
			ControlChangeType_LSBForControl10_Pan = 0x2A,
			ControlChangeType_LSBForControl11_ExpressionController = 0x2B,
			ControlChangeType_LSBForControl12_EffectControl1 = 0x2C,
			ControlChangeType_LSBForControl13_EffectControl2 = 0x2D,
			ControlChangeType_LSBForControl14_Undefined = 0x2E,
			ControlChangeType_LSBForControl15_Undefined = 0x2F,
			ControlChangeType_LSBForControl16_GeneralPurposeController1 = 0x30,
			ControlChangeType_LSBForControl17_GeneralPurposeController2 = 0x31,
			ControlChangeType_LSBForControl18_GeneralPurposeController3 = 0x32,
			ControlChangeType_LSBForControl19_GeneralPurposeController4 = 0x33,
			ControlChangeType_LSBForControl20_Undefined = 0x34,
			ControlChangeType_LSBForControl21_Undefined = 0x35,
			ControlChangeType_LSBForControl22_Undefined = 0x36,
			ControlChangeType_LSBForControl23_Undefined = 0x37,
			ControlChangeType_LSBForControl24_Undefined = 0x38,
			ControlChangeType_LSBForControl25_Undefined = 0x39,
			ControlChangeType_LSBForControl26_Undefined = 0x3A,
			ControlChangeType_LSBForControl27_Undefined = 0x3B,
			ControlChangeType_LSBForControl28_Undefined = 0x3C,
			ControlChangeType_LSBForControl29_Undefined = 0x3D,
			ControlChangeType_LSBForControl30_Undefined = 0x3E,
			ControlChangeType_LSBForControl31_Undefined = 0x3F,
			ControlChangeType_DamperPedalOnOff = 0x40,
			ControlChangeType_PortamentoOnOff = 0x41,
			ControlChangeType_SostenutoOnOff = 0x42,
			ControlChangeType_SoftPedalOnOff = 0x43,
			ControlChangeType_LegatoFootswitch = 0x44,
			ControlChangeType_Hold = 0x45,
			ControlChangeType_SoundController1_SoundVariation = 0x46,
			ControlChangeType_SoundController2_TimbreHarmonicIntens = 0x47,
			ControlChangeType_SoundController3_ReleaseTime = 0x48,
			ControlChangeType_SoundController4_AttackTime = 0x49,
			ControlChangeType_SoundController5_Brightness = 0x4A,
			ControlChangeType_SoundController6_DecayTime = 0x4B,
			ControlChangeType_SoundController7_VibratoRate = 0x4C,
			ControlChangeType_SoundController8_VibratoDepth = 0x4D,
			ControlChangeType_SoundController9_VibratoDelay = 0x4E,
			ControlChangeType_SoundController10_Undefined = 0x4F,
			ControlChangeType_GeneralPurposeController5 = 0x50,
			ControlChangeType_GeneralPurposeController6 = 0x51,
			ControlChangeType_GeneralPurposeController7 = 0x52,
			ControlChangeType_GeneralPurposeController8 = 0x53,
			ControlChangeType_PortamentoControl = 0x54,
			ControlChangeType_HighResolutionVelocityPrefix = 0x58,
			ControlChangeType_Effects1Depth = 0x5B,
			ControlChangeType_Effects2Depth_TremoloDepth = 0x5C,
			ControlChangeType_Effects3Depth = 0x5D,
			ControlChangeType_Effects4Depth_CelesteDetuneDepth = 0x5E,
			ControlChangeType_Effects5Depth_PhaserDepth = 0x5F,
			ControlChangeType_DataIncrement_DataEntryPlus1 = 0x60,
			ControlChangeType_DataDecrement_DataEntryMinus1 = 0x61,
			ControlChangeType_NonRegistered_ParameterNumberLSB = 0x62,
			ControlChangeType_NonRegistered_ParameterNumberMSB = 0x63,
			ControlChangeType_Registered_ParameterNumberLSB = 0x64,
			ControlChangeType_Registered_ParameterNumberMSB = 0x65,
			ControlChangeType_AllSoundOff = 0x78,
			ControlChangeType_ResetAllController = 0x79,
			ControlChangeType_LocalControl = 0x7A,
			ControlChangeType_AllNoteOff = 0x7B,
			ControlChangeType_OmniOn = 0x7C,
			ControlChangeType_OmniOff = 0x7D,
			ControlChangeType_PolyphonicOn = 0x7E,
			ControlChangeType_MonoPhonicOn = 0x7F,
		};

		static std::string GetFirstCategoryName( FirstCategory category );

		static std::string GetSecondCategoryName( SecondCategory category );

		static std::string GetTypeName( Type type );

		static std::string GetControlChangeTypeName( ControlChangeType type );

		static uint8_t GetControlBitOfChannelModeMessage( Type type );

		static int GetDataByteCount( Type type );

		static bool IsChannelMessage( Type type );

		static bool IsSystemMessage( Type type );

		static Type GetChannelModeMessageType( uint8_t value );

	public:

		MidiMessage();

		MidiMessage( Type type );

		MidiMessage( Type type, uint8_t firstDataByte );

		MidiMessage( Type type, uint8_t firstDataByte, uint8_t secondDataByte );

		~MidiMessage() {}

		void SetType( Type typeArg ) { type = typeArg; }

		void SetChannelId( uint8_t channelIdArg ) { channelId = channelIdArg; }

		void SetDataByte( uint8_t byte ) { dataBytes[ 0 ] = byte; }

		void SetDataByte( uint8_t byte1st, uint8_t byte2nd ) { dataBytes[ 0 ] = byte1st; dataBytes[ 1 ] = byte2nd; }

		FirstCategory GetFirstCategory() const;

		SecondCategory GetSecondCategory() const;

		Type GetType() const { return type; }

		uint8_t GetChannelId() const { return channelId; }

		uint8_t Get1stDataByte() const { return dataBytes[ 0 ]; }

		uint8_t Get2ndDataByte() const { return dataBytes[ 1 ]; }

		std::string ToString( int tabCount = 0, bool varbose = false ) const;

	private:

		Type type;

		uint8_t channelId;			//	used only for channel message

		uint8_t dataBytes[ 2 ];
	};
}
